// Fluxions 3D Engine
// vector4.hpp
// Copyright (C) 2000-2011 Jonathan Metzgar
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#ifndef FLUXIONS_VECTOR4_HPP
#define FLUXIONS_VECTOR4_HPP

#include "math.hpp"

namespace Fluxions
{
	template <typename X>
	class TVector4
	{
	public:
		union
		{
			struct
			{
				X x, y, z, w;
			};
			struct
			{
				X r, g, b, a;
			};
			struct
			{
				X s, t, p, q;
			};
			struct
			{
				X A, B, C, D;
			};
			struct
			{
				X v[4];
			};
		};

		TVector4();
		TVector4(X _x, X _y, X _z, X _w);
		TVector4(const TVector4<X> & V);
		TVector4(const X V[4]);
		TVector4(const TVector3<X> & V);

		template <typename T> const TVector4<X> & operator = (const TVector4<T> & V);
		template <typename T> const TVector4<X> & operator = (const TVector3<T> & V);

		template <typename T> operator TVector4<T> () const;

		template <typename T> const TVector4<X> & operator *= (const T val);
		template <typename T> const TVector4<X> & operator /= (const T val);
		template <typename T> const TVector4<X> & operator += (const TVector4<T> & V);
		template <typename T> const TVector4<X> & operator -= (const TVector4<T> & V);

		template <typename T> TVector4<X> operator * (const T val) const;
		template <typename T> TVector4<X> operator / (const T val) const;
		template <typename T> TVector4<X> operator + (const TVector4<T> & V) const;
		template <typename T> TVector4<X> operator - (const TVector4<T> & V) const;

		template <typename T> const X operator * (const TVector4<T> & V) const;

		const X Length() const;
		const X LengthSquared() const;
		void Normalize();
	};
	
	//////////////////////////////////////////////////////////////////
	// I M P L E M E N X A X I O N ///////////////////////////////////
	//////////////////////////////////////////////////////////////////

	template<typename X>
	TVector4<X>::TVector4()
	{
	}

	template <typename X>
	TVector4<X>::TVector4(X _x, X _y, X _z, X _w)
		: x(_x), y(_y), z(_z), w(_w)
	{
	}

	template <typename X>
	TVector4<X>::TVector4(const TVector4<X> & V)
		: x(V.x), y(V.y), z(V.z), w(V.w)
	{
	}

	template <typename X>
	TVector4<X>::TVector4(const X V[4])
		: x(V[0]), y(V[1]), z(V[2]), w(V[3])
	{
	}

	template <typename X>
	TVector4<X>::TVector4(const TVector3<X> & V)
		: x(V.x), y(V.y), z(V.z), w(1)
	{
	}

	template <typename X>
	template <typename T> 
	const TVector4<X> & TVector4<X>::operator = (const TVector4<T> & V)
	{
		x = V.x;
		y = V.y;
		z = V.z;
		w = V.w;
		return *this;
	}

	template <typename X>
	template <typename T> 
	const TVector4<X> & TVector4<X>::operator = (const TVector3<T> & V)
	{
		x = V.x;
		y = V.y;
		z = V.z;
		w = 1;
		return *this;
	}

	template <typename X>
	template <typename T>
	TVector4<X>::operator TVector4<T> () const
	{
		return TVector4<T>(x, y, z, w);
	}

	template <typename X>
	template <typename T> 
	const TVector4<X> & TVector4<X>::operator *= (const T val)
	{
		x *= val;
		y *= val;
		z *= val;
		w *= val;
		return *this;
	}

	template <typename X>
	template <typename T> 
	const TVector4<X> & TVector4<X>::operator /= (const T val)
	{
		x /= val;
		y /= val;
		z /= val;
		w /= val;
		return *this;
	}

	template <typename X>
	template <typename T> 
	const TVector4<X> & TVector4<X>::operator += (const TVector4<T> & V)
	{
		x += V.x;
		y += V.y;
		z += V.z;
		w += V.w;
		return *this;
	}

	template <typename X>
	template <typename T> 
	const TVector4<X> & TVector4<X>::operator -= (const TVector4<T> & V)
	{
		x -= V.x;
		y -= V.y;
		z -= V.z;
		w -= V.w;
		return *this;
	}

	template <typename X>
	template <typename T> 
	TVector4<X> TVector4<X>::operator * (const T val) const
	{
		return TVector4<X>(x * val, y * val, z * val, w * val);
	}

	template <typename X>
	template <typename T> 
	TVector4<X> TVector4<X>::operator / (const T val) const
	{
		return TVector4<X>(x / val, y / val, z / val, w / val);
	}

	template <typename X>
	template <typename T> 
	TVector4<X> TVector4<X>::operator + (const TVector4<T> & V) const
	{
		return TVector4<X>(x + V.x, y + V.y, z + V.z, w + V.w);
	}

	template <typename X>
	template <typename T> 
	TVector4<X> TVector4<X>::operator - (const TVector4<T> & V) const
	{
		return TVector4<X>(x - V.x, y - V.y, z - V.z, w - V.w);
	}

	template <typename X>
	template <typename T> 
	const X TVector4<X>::operator * (const TVector4<T> & V) const
	{
		return x * V.x + y * V.y + z * V.z + w * V.w;
	}

	template <typename X>
	const X TVector4<X>::Length() const
	{
		return sqrt(x * x + y * y + z * z + w * w);
	}

	template <typename X>
	const X TVector4<X>::LengthSquared() const
	{
		return x * x + y * y + z * z + w * w;
	}

	template <typename X>
	void TVector4<X>::Normalize()
	{
		*this /= Length();
	}

	template <typename T>
	T DotProduct(const TVector4<T> & v1, const TVector4<T> & v2)
	{
		T out;
		out = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w;
	}

	typedef TVector4<float> Vector4f;
	typedef TVector4<double> Vector4d;
}

#endif
